package com.tjy.core;

import com.tjy.constant.Constant;
import com.tjy.util.FileUtils;
import com.tjy.util.HttpUtils;
import com.tjy.util.LogUtils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.concurrent.*;

/**
 * ClassName:Downloader
 * Description:文件下载器
 * author: jiuyang
 * DateTime: 2024/8/13 下午4:51
 */
public class Downloader {

    private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
    private ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(Constant.THREAD_NUM, Constant.THREAD_NUM, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(Constant.THREAD_NUM));
    private CountDownLatch countDownLatch = new CountDownLatch(Constant.THREAD_NUM);
    public void download(String url) {
        //获取文件名
        String httpFileName = HttpUtils.getFileName(url);
        //文件下载路径
        httpFileName = Constant.PATH + httpFileName;
        //获取本地文件大小
        long localFileLength = FileUtils.getFileContentLength(httpFileName);

        //获取连接对象
        HttpURLConnection httpURLConnection = null;
        DownloaderInfo downloaderInfo;
        try {
            httpURLConnection = HttpUtils.getHttpURLConnection(url);
            //获取下载文件总大小
            int contentLength = httpURLConnection.getContentLength();

            if (localFileLength >= contentLength) {
                LogUtils.info("{}已下载完毕，无需重新下载", httpFileName);
                return;
            }
            // 创建获取下载信息的任务对象
            downloaderInfo = new DownloaderInfo(contentLength);
            executorService.scheduleAtFixedRate(downloaderInfo, 1, 1, TimeUnit.SECONDS);
            // 切分任务
            ArrayList<Future> list = new ArrayList<>();
            spilt(url, list);
            //记录线程执行
            countDownLatch.await();
            //合并文件
            if (merge(httpFileName)) {
                clearTemp(httpFileName);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.print("\r");
            System.out.print("下载完成");
            //关闭连接对象
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
            executorService.shutdownNow();
            //关闭线程池
            poolExecutor.shutdown();
        }
    }

    /**
     * @description: 文件切分下载
     * @date 2024/8/14 下午4:57
     */
    public void spilt(String url, ArrayList<Future> futures) {

        try {
            //获取文件大小
            long contentLength = HttpUtils.getHttpFileContentLength(url);
            //计算切分后的文件大小
            long size = contentLength / Constant.THREAD_NUM;
            //计算分块个数
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                //计算下载起始位置
                long startPos = i * size;
                long endPos;
                if (i == Constant.THREAD_NUM - 1) {
                    endPos = 0;
                } else {
                    endPos = startPos + size;
                }
                if (startPos != 0) {
                    startPos++;
                }
                //创建任务对象
                DownloaderTask downloaderTask = new DownloaderTask(url, startPos, endPos, i,countDownLatch);
                //把任务提交到线程池中
                Future<Boolean> submit = poolExecutor.submit(downloaderTask);
                futures.add(submit);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @description: 合并文件
     * @date 2024/8/15 下午3:32
     */
    public boolean merge(String fileName) {
        LogUtils.info("开始合并文件{}", fileName);
        byte[] buffer = new byte[Constant.BYTE_SIZE];
        int len;
        try (RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw")) {
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(Paths.get(fileName + ".temp" + i)))) {
                    while ((len = bis.read(buffer)) != -1) {
                        accessFile.write(buffer, 0, len);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            LogUtils.info("文件合并完毕{}", fileName);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
    * @description: 清楚临时文件
    * @date 2024/8/15 下午3:34
    */
    public boolean clearTemp(String fileName) {
        for (int i = 0; i < Constant.THREAD_NUM; i++) {
            File file = new File(fileName + ".temp" + i);
            boolean delete = file.delete();
        }
        return true;
    }
}
